﻿@using System.Threading
@using ExpressiveAnnotations.MvcWebSample.Misc
@model ExpressiveAnnotations.MvcWebSample.Models.Query

@{
    var lang = Thread.CurrentThread.CurrentCulture.TwoLetterISOLanguageName;
    var client = "client".Equals(ViewBag.Validation);
    var indication = ViewBag.Indication;
    var triggers = ViewBag.Triggers;
    var verbose = ViewBag.Verbose;
}

<div class="box">
    <div class="left-corner">
        <h1>ExpressiveAnnotations - annotation-based conditional validation</h1>
    </div>
    <div class="right-corner">
        <a href="https://github.com/jwaliszko/ExpressiveAnnotations">[more on github]</a>
    </div>
</div>

@using (Html.BeginForm("Index", "Home", FormMethod.Post, new {id = "mainform"}))
{
    @Html.AntiForgeryToken()
    @Html.ValidationSummary(true) @*show/hide validation summary block*@

    <div class="box-narrow">
        <div class="right-corner">
            <div class="message-success">@(ViewBag.Success ?? Html.Raw("&nbsp;"))</div>
        </div>
    </div>

    <fieldset>
        <legend>Usage sample for model validation in ASP.NET MVC</legend>
        <div id="panel" class="box-small">
            @Html.Partial("_Culture")
            @Html.Partial("_Validation")
            @Html.Partial("_Indications")
            @Html.Partial("_EAOptions")
        </div>
        @Html.EditorFor(model => model)
        <input type="submit" value="Submit" />
    </fieldset>
}

@section Scripts {
    @if (client)
    {
        @Scripts.Render("~/bundles/jqueryval")
        @Scripts.Render("~/bundles/expressive") // load expressive.annotations.validate.js

        <script>
            // var expann = ea.noConflict(); // in case of naming collision, return control of the ea variable back to its origins (old references of ea are saved during ExpressiveAnnotations initialization; noConflict() restores them)
            // expann.addMethod... // do something with ExpressiveAnnotations
            // ea... // do something with original ea variable

            // ----- define your settings -----

            ea.settings.apply({
                debug: '@verbose' === 'True',
                optimize: '@indication' === 'no-annotating',
                enumsAsNumbers: true,
                dependencyTriggers: '@triggers' // if not explicitly provided, default setup is: 'change keyup'
            });

            // ----- define your parsers -----

            ea.addValueParser('NonStandardDateParser', function(value) { // override default datetime parsing mechanism to deal with yyyy-mm-dd format
                if (value === '') {
                    return;
                }

                var millisec;
                if (!/^\d{4}-\d{2}-\d{2}$/.test(value)) { // in case the value does not have non-standard format (here, when language set to english)...
                    millisec = Date.parse(value); // ...try to go with standard way (default date parser used)
                } else {
                    var arr = value.split('-');
                    var date = new Date(arr[0], arr[1] - 1, arr[2]); // months are 0-based
                    millisec = date.getTime();
                }

                if (typeof millisec === 'number' && !isNaN(millisec)) {
                    return millisec; // return milliseconds...
                }

                throw 'Invalid date string provided.';
            });
            ea.addValueParser('ArrayParser', function(value, field) { // provide your custom parsing mechanism of the field value at client side
                                                                      // parameters: value - raw data string extracted by default from DOM element
                                                                      //             field - DOM element name for which parser was invoked
                var map = $(':input[name="' + field + '"]').serializeArray();
                var arr = [];
                $.each(map, function() {
                    arr.push(this.value);
                });
                return arr.length === 0
                    ? null  // if array is empty, return null for RequiredIf to complain
                    : arr;  // otherwise - return array object for the ArrayLength method to work on
            });

            // ----- define your methods -----

            ea.addMethod('IsBloodType', function(group) {
                return /^(A|B|AB|0)[\+-]$/.test(group);
            });
            ea.addMethod('AddYears', function(from, years) {
                from = new Date(from); // preprocessed datetime arguments passed to functions are given milliseconds
                var to = from.getFullYear() + years;
                from.setFullYear(to);
                return from.getTime();
            });
            ea.addMethod('IntArrayLength', function(array) {
                return array.length;
            });
            ea.addMethod('StringArrayLength', function(array) {
                return array.length;
            });
            ea.addMethod('IntArrayContains', function(value, array) {
                return $.inArray(value, array) >= 0;
            });

            // ----- do some other stuff, not necessarily related to ea directly -----

            function Mimic(src, dst) { // clones the value and behaviour of src field into dst one
                $('input[name="' + src + '"]').on(ea.settings.dependencyTriggers,
                    function(e) {
                        var $src = $(this);
                        var value = $src.attr('type') === 'checkbox' ? $src.is(':checked') : $src.val();
                        $('input[name="' + dst + '"]').val(value);
                        $('input[name="' + dst + '"]').trigger(e.type); // trigger the change for dependency validation
                    });
            }

            $(document).ready(function() {
                var validator = $('form').data('validator'); // get validator associated with the form (association established by jquery.validate.unobtrusive.js)
                validator.settings.onkeyup = function(element) { // execute validation of current field on keyup event
                    $(element).valid();
                }

                $('#triggers input[type="checkbox"]').on('click', function() {
                    // disable some excessive activity if you wish, and e.g. trigger dependent fields validation only when field 'change' event is fired -
                    // - when all triggers are disabled, validation will be fired on form submit attempt only
                    var triggers = $('#triggers input[type="checkbox"]:checked').map(function() {
                        return $(this).attr('data-val');
                    }).toArray().join(' ');

                    $.ajax({ // set cookie (could be also done directly here, in JavaScript: http://stackoverflow.com/a/17521905/270315)
                        type: 'POST',
                        dataType: 'json',
                        contentType: 'application/json; charset=utf-8',
                        url: '/System/SetTriggers',
                        data: JSON.stringify({ events: triggers }),
                        success: function(result) {
                            if (result.success) {
                                ea.settings.apply({ dependencyTriggers: triggers });
                            }
                        }
                    });
                });

                $('#debug input[type="checkbox"]').on('click', function() {
                    var verbose = $('#debug input[type="checkbox"]').is(':checked');

                    $.ajax({
                        type: 'POST',
                        dataType: 'json',
                        contentType: 'application/json; charset=utf-8',
                        url: '/System/SetVerbosity',
                        data: JSON.stringify({ value: verbose }),
                        success: function(result) {
                            if (result.success) {
                                ea.settings.apply({ debug: verbose });
                            }
                        }
                    });
                });

                $(document).on('paste', function(e) {
                    e = $.extend({}, e, { type: 'afterpaste' }); // define custom event 'afterpaste', which fires slightly after 'paste' event is invoked - this tiny delay is necessary for the field value to become ready for being collected
                    window.setTimeout(function() { // ('paste' event itself is to early, because fires before field value is actually modified, so its new content value cannot be accessed from within this event)
                        $(e.target).trigger(e.type);
                    }, 50);
                });

                Mimic('@Html.NameFor(m => m.GoAbroad)',
                      '@Html.NameFor(m => m.ContactDetails.Parent.GoAbroad)');

                if ('@indication' !== 'no-annotating') {
                    annotateRequiredFields('@indication' === 'asterisks-with-hiding');
                }
            });

            function annotateRequiredFields(hideNonRequired) { // an example of how eavalid event can be used (annotate required fields, hide non-required fields, etc.)
                var $form = $('form');
                var selector = 'input, select, textarea';

                if (hideNonRequired) {
                    var validator = $form.validate();
                    validator.settings.ignore = ''; // enable validation for ':hidden' fields (to have hidden fields visibility re-computed)
                }

                $form.find(selector).on('eavalid', function(e, type, valid, expr, cond, idx) { // verify asterisk visibility based on computed condition
                    if (type === 'requiredif' && cond !== undefined) {
                        if (idx === 0) { // if first of its kind...
                            e.currentTarget.eacond = undefined; // ...reset asterisk visibility flag
                        }
                        e.currentTarget.eacond |= cond; // multiple requiredif attributes can be applied to a single field - remember if any condition is true
                        var li = $(e.currentTarget).closest('li');
                        var asterisk = li.find('.asterisk');

                        if (e.currentTarget.eacond) {
                            asterisk.show(); // show asterisk for required fields (no matter if valid or not)
                            if (hideNonRequired) {
                                li.show();
                            }
                        }
                        else {
                            asterisk.hide();
                            if (hideNonRequired) {
                                li.hide(); // hide non-required fields
                            }
                        }
                    }
                });

                $form.find(selector).each(function() { // apply asterisks for required or conditionally required fields
                    if ($(this).attr('data-val-required') !== undefined // make sure implicit required attributes for value types are disabled i.e. DataAnnotationsModelValidatorProvider.AddImplicitRequiredAttributeForValueTypes = false
                        || $(this).attr('data-val-requiredif') !== undefined) {
                        var div = $(this).parent('div');
                        div.prepend('<span class="asterisk">*</span>');
                    }
                });

                $form.find(selector).each(function() { // run validation for every field to verify asterisks visibility
                    if ($(this).attr('data-val-requiredif') !== undefined) {
                        $(this).valid();
                        $(this).removeClass('input-validation-error');
                        $(this).parent().find('.field-validation-error').empty();
                    }
                });
            }
        </script>
    }

    <script>
        $(document).ready(function() {
            $('.action').click(function() {
                $(this).hide();
                $(this).parent().find('.code').toggle('slow');
            });

            $.datepicker.setDefaults({
                changeMonth: true,
                changeYear: true
            });

            $('.date').datepicker($.datepicker.regional['@lang']).on('change', function() {
                if (typeof $('form').valid == 'function') { // safety check if our demo project is not set to server side validation only (no jquery.validate.js loaded)
                    $(this).valid(); // triggers the validation test on change (to avoid second click)
                }
            });

            $('input[type="checkbox"], input[type="radio"]').on('change', function() {
                if (typeof $('form').valid == 'function') {
                    $(this).valid(); // triggers the validation test on change (jquery.validate.js does not trigger checkbox and radio validation on click, if such a click occurs prior to first submission attempt of entire form - later, validation is triggered
                }
            }); // (downside of this hack - validation triggered 2 times for these fields after first form submission attempt is done)

            $('form').on('submit', function() {
                $(".message-success").hide();
            });
        });

        $.datepicker.regional['pl'] = { // https://github.com/jquery/jquery-ui/tree/master/ui/i18n
            closeText: 'Zamknij',
            prevText: '&#x3C;Poprzedni',
            nextText: 'Następny&#x3E;',
            currentText: 'Dziś',
            monthNames: ['Styczeń', 'Luty', 'Marzec', 'Kwiecień', 'Maj', 'Czerwiec', 'Lipiec', 'Sierpień', 'Wrzesień', 'Październik', 'Listopad', 'Grudzień'],
            monthNamesShort: ['Sty', 'Lu', 'Mar', 'Kw', 'Maj', 'Cze', 'Lip', 'Sie', 'Wrz', 'Pa', 'Lis', 'Gru'],
            dayNames: ['Niedziela', 'Poniedziałek', 'Wtorek', 'Środa', 'Czwartek', 'Piątek', 'Sobota'],
            dayNamesShort: ['Nie', 'Pn', 'Wt', 'Śr', 'Czw', 'Pt', 'So'],
            dayNamesMin: ['N', 'Pn', 'Wt', 'Śr', 'Cz', 'Pt', 'So'],
            weekHeader: 'Tydz',
            dateFormat: 'yy-mm-dd',
            firstDay: 1,
            isRTL: false,
            showMonthAfterYear: false,
            yearSuffix: ''
        };
    </script>
}
